#!/usr/bin/env python
import up2
import readline
import datetime                                                                                        
import json
import requests
import os
import subprocess
import sys
import hashlib
from functools import reduce
from hashlib import md5
import urllib.parse
import time
mixinKeyEncTab = [
    46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
    33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
    61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
    36, 20, 34, 44, 52
]


def is_night_time():
    now = datetime.datetime.now().time()
    print(now)
    start_time = datetime.time(23, 00)  # 22:00
    end_time = datetime.time(23, 30)   # 6:30
    if now >= start_time and now <= end_time:
        return True
    return False


def getMixinKey(orig: str):
    '对 imgKey 和 subKey 进行字符顺序打乱编码'
    return reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32]


def encWbi(params: dict, img_key: str, sub_key: str):
    '为请求参数进行 wbi 签名'
    mixin_key = getMixinKey(img_key + sub_key)
    curr_time = round(time.time())
    params['wts'] = curr_time                                   # 添加 wts 字段
    params = dict(sorted(params.items()))                       # 按照 key 重排参数
    # 过滤 value 中的 "!'()*" 字符
    params = {
        k : ''.join(filter(lambda chr: chr not in "!'()*", str(v)))
        for k, v 
        in params.items()
    }
    query = urllib.parse.urlencode(params)                      # 序列化参数
    wbi_sign = md5((query + mixin_key).encode()).hexdigest()    # 计算 w_rid
    params['w_rid'] = wbi_sign
    return params

def getWbiKeys():
    '获取最新的 img_key 和 sub_key'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
        'Referer': 'https://www.bilibili.com/'
    }
    resp = requests.get('https://api.bilibili.com/x/web-interface/nav', headers=headers)
    resp.raise_for_status()
    json_content = resp.json()
    img_url: str = json_content['data']['wbi_img']['img_url']
    sub_url: str = json_content['data']['wbi_img']['sub_url']
    img_key = img_url.rsplit('/', 1)[1].split('.')[0]
    sub_key = sub_url.rsplit('/', 1)[1].split('.')[0]
    return img_key, sub_key


img_key, sub_key = getWbiKeys()


signed_params = encWbi(
    params={
        'foo': '114',
        'bar': '514',
        'baz': 1919810
    },
    img_key=img_key,
    sub_key=sub_key
)
query = urllib.parse.urlencode(signed_params)
print(signed_params)
print(query)
cookies = {
    "b_nut": "1724244395",
    "bili_jct": "096d08f41c0fabf8752cb38584725635",
    "LIVE_BUVID": "AUTO6489616798740742",
    "PVID": "1",
    "header_theme_version": "undefined",
    "browser_resolution": "839-959",
    "_uuid": "EFCFC575-D3EE-28510-49BC-971B52DD7BCB95716infoc",
    "enable_web_push": "DISABLE",
    "DedeUserID__ckMd5": "d7206945b5ae73fa",
    "sec_ck": "BmeOONcXq57ruqUr1QDb27A8cUrbX4RvMV70-7cOhAmhFxhL6Q0twjGnX3aygXYpbmM-U7uLz2GkYrI1FRq_je2AEln-yHp50TFdAOTHgeF83g6inNO7P4C2CJNNxLYI24PVjqG_Xwi9yc_gp5V1JLnvCG0AY2IPJSVH9Nh5gnuIsaP28lyCYEPfY0uodm6O3khkgLDpwQZ1QSMqrCyFsv1ZzD0Df3F39waRySbOih35PuTFXFVxSSUjw1qBC-SEzJ_ETGN-6_RrwAqSK0nvI6qQb58!Mss",
    "sid": "7kffja5x",
    "buvid4": "BE546718-40D5-C219-14C9-4738DB75FD9096535-024082112-W1vzNLu6y373n/T9EEhHU9iaaNex72Rwh/K3fBugXf0",
    "DedeUserID": "3546729777465452",
    "buvid_fp": "30c3020be6cee8345ddc4c3c6b77f60f",
    "buvid3": "27C4C937-845E-D878-D4B3-890A2A3D391A96535infoc",
    "b_lsid": "3EB998D8_1917CBB0393",
    "bili_ticket": "eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjA5NjE3MzYsImlhdCI6MTc2MDcwMjQ3NiwicGx0IjotMX0.Y0Gn8l_34Ca-4gU202fV62MeuONZCO2MTI5WmXuPm2Q",
    "home_feed_column": "4",
    "bili_ticket_expires": "1760961676",
    "SESSDATA": "709b94eb,1776254536,94f22*a2CjCa83JEzOzd-7GgtnmWTgznfuKSmR7f_9A4d0uQbMgKFYe4Uo6O4ZdGnfDjsQ9LdiASVnM1Q0N2clgtc2pQZVlsTHZVOS1Rd3o3MGZqblZaQ28tZWwwSUZLZXJ6UnZzS3hVZGdxYVZoSXlQWk1ZZ19zVWpKMUpNdmRyMG1jV3FMeEVXb3ZkN25BIIEC"
}

refresh_token = "87587212a545dc0d695e9388cbd45fa2"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    'Referer': 'https://www.bilibili.com/'
}#通用请求头

def search_bilibili_ups(keyword, page=1, sessdata="45040035%2C1773275386%2C3b45b%2A92CjDGsvSN9BqK2kzhJxmNUazA6155U0NYB2OGLF9_0F9a7uassJucIyRQQmgm1L2qo78SVjRCb2k2WGFwRzcyT3ZXa3lPanN3TVdsVzA3YVp4azRFRGphSDRKRGh1Wmc3WmlEcVlkTW1uZ2tDa0NfY3ptY3N0UFV5QjcyR2lVRXVsTmIwS0dYajRRIIEC"):
    """
    搜索B站视频

    Args:
        keyword (str): 搜索关键词
        page (int): 页码
        sessdata (str): SESSDATA cookie值
        
    Returns:
        list: 视频列表，包含标题和链接
    """
    url = 'https://api.bilibili.com/x/web-interface/search/type?'+query
    
    params = {
        'search_type': 'bili_user',
        'keyword': keyword,
        'order': 'totalrank',
        'duration': '0',
        'tids': '0',
        'page': str(page)
    }   
    try:
        print(f"正在搜索: {keyword}...")
        response = requests.get(url, params=params, cookies=cookies, timeout=10,headers=headers)
        response.raise_for_status()    
        data = response.json()
        print(response.text)
        
        if data.get('code') != 0:
            print(f"API返回错误: {data.get('message')}")
            return []
        results = data['data']['result']
        print(results)
        users = data.get("data", {}).get("result", [])
        i = 1
        mids=[]
        for user in users:
            uid = user.get("mid")
            mids.append(uid)
            uname = user.get("uname")
            print(i, end="")
            print(f"UID: {uid}, 用户名: {uname}")
            i = i + 1
        choice=int(input("请输入你选择的用户序号"))
        display_up_videos(mids[choice-1])
    except requests.RequestException as e:
        print(f"网络请求错误: {e}")
        return []
    except KeyError:
        print("未搜索到用户")

def search_bilibili_videos(keyword, page=1):
    """
    搜索B站视频
    
    Args:
        keyword (str): 搜索关键词
        page (int): 页码
        sessdata (str): SESSDATA cookie值
        
    Returns:
        list: 视频列表，包含标题和链接
    """
    url = 'https://api.bilibili.com/x/web-interface/search/type?'+query
    
    params = {
        'search_type': 'video',
        'keyword': keyword,
        'order': 'totalrank',
        'duration': '0',
        'tids': '0',
        'page': str(page)
    }
    try:
        print(f"正在搜索: {keyword}...")
        response = requests.get(url, params=params, cookies=cookies, timeout=10,headers=headers)
        response.raise_for_status()
        
        data = response.json()
        
        if data.get('code') != 0:
            print(f"API返回错误: {data.get('message')}")
            return []
        
        results = data['data']['result']
        video_list = []
        print(results)        
        for video in results:
            if video.get('type') == 'video':
                video_info = {
                    'title': video['title'].replace('<em class="keyword">', '').replace('</em>', ''),
                    'url': video['arcurl'],
                    'bvid': video.get('bvid', ''),
                    'aid': video.get('aid', ''),
                    'author':video['author']
                }
                video_list.append(video_info)
        
        return video_list
        
    except requests.RequestException as e:
        print(f"网络请求错误: {e}")
        return []
    except Exception as e:
        print(f"搜索错误: {e}")
        return []
def display_up_videos(mid):
    url = "https://api.bilibili.com/x/series/recArchivesByKeywords?mid="+str(mid)+"&keywords=&ps=100"+"&"+query
    
    
    
    response=requests.get(url=url,cookies=cookies,headers=headers)
    response.raise_for_status()
    data=response.json()
    result = data['data']['archives']
    avids = []
    i = 1
    for video in result:
        print(str(i)+"."+video['title'])
        avids.append(video['aid'])
        i = i + 1
    choicevideo = int(input("请输入要下载的视频序号"))
    pagevideohelper(avids[choicevideo-1])


def display_videos(video_list):
    """
    显示视频列表
    Args:
        video_list (list): 视频列表
    """
    if not video_list:
        print("没有找到相关视频！")
        return
    
    print(f"\n找到 {len(video_list)} 个视频:")
    print("=" * 80)
    
    for i, video in enumerate(video_list, 1):
        print(f"{i:2d}. {video['title']}")
        print(f"    链接: {video['url']}")
        print(f"    UP主：{video['author']}")
        print()
#    ./ffmpeg/ffmpeg -i video.mp4 -i audio.mp3 -c copy -map 0:v -map 1:a output_fast.mp4                                                     
def download_video(video_url, output_dir="./downloads"):
    """
    使用you-get下载视频
    
    Args:
        video_url (str): 视频链接
        output_dir (str): 输出目录
    """
    # 创建输出目录
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    try:
        print(f"开始下载: {video_url}")
        print("下载中...（这可能需要一些时间）")
        
        # 使用you-get下载视频，显示实时输出
        cmd = [
            'you-get', 
            # 输出到当前目录
             '-O', 
             'video.mp4',
              # 指定文件名
         # 指定输出目录
            video_url,
            '--debug'
        ]
#shell@ab671103ab0f ~> ./ffmpeg/ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -c:a aac -strict experimental output.mp4        
        # 实时显示you-get的输出
        process = subprocess.Popen(
            cmd, 
            stdout=subprocess.PIPE, 
            stderr=subprocess.STDOUT,  # 将stderr重定向到stdout
            universal_newlines=True,   # 文本模式
            bufsize=1                  # 行缓冲
        )
        
        # 实时读取并打印输出
        while True:
            output = process.stdout.readline()
            if output == '' and process.poll() is not None:
                break
            if output:
                print(output.strip())
        
        # 等待进程结束并获取返回码
        return_code = process.wait()
        
        if return_code == 0:
            print("下载完成！")
            print(f"视频已保存到: {output_dir}")
            #merge_video()
            up2.uploadfile("video.mp4.mp4") 
            os.system("rm video.mp4.mp4")
        else:
            print("下载失败！")
            print(f"返回码: {return_code}")
            
    except subprocess.TimeoutExpired:
        print("下载超时！")
    except Exception as e:
        print(f"下载错误: {e}")
        
def check_you_get_installed():
    """
    检查you-get是否已安装
    """
    try:
        subprocess.run(['you-get', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False

def install_you_get():
    """
    安装you-get
    """
    print("检测到you-get未安装，正在安装...")
    try:
        subprocess.run([sys.executable, '-m', 'pip', 'install', 'you-get'], check=True)
        print("you-get安装成功！")
        return True
    except subprocess.CalledProcessError:
        print("安装失败，请手动安装: pip install you-get")
        return False


def searchup():
    keyword = input("请输入要搜索的用户名")
    search_bilibili_ups(keyword)


def getvideonum(aid):
    url = "https://api.bilibili.com/x/web-interface/view?aid="+str(aid)
    response = requests.get(url=url, headers=headers)
    return response.json()["data"]["videos"]


def getvideopages(aid):
    url = "https://api.bilibili.com/x/web-interface/view?aid="+str(aid)
    response = requests.get(url=url, headers=headers)
    pagenum = response.json()["data"]["videos"]
    pageinfo = response.json()["data"]["pages"]
    pages = []
    for i in range(pagenum):
        pages.append(pageinfo[i]["part"])
    return pages


def pagevideohelper(aid):
    if getvideonum(aid) == 1:
        # 选择下载目录
        output_dir = "./"
        
        # 开始下载
        download_video("https://www.bilibili.com/video/av"+str(aid), output_dir)
        exit()
    else:
        print("这是一个多页视频，请选择页")
        pages=getvideopages(aid)
        for i in range(len(pages)):
            print(str(i+1)+"."+pages[i])
        page=input("请输入序号")
        download_video("http://www.bilibili.com/video/av"+str(aid)+"?p="+page)
    pass
def merge_danmu():
    import danmu
    danmu.run_danmaku2ass("video.mp4.mp4")

def searchvideo():
    """
    搜索视频
    """
    # 检查you-get是否安装
    
    print("=" * 50)
    print("B站视频搜索下载工具")
    print("=" * 50)
    
    # 获取搜索关键词
    keyword = input("请输入搜索关键词: ").strip()
    if not keyword:
        print("关键词不能为空！")
        return
    
    # 搜索视频
    videos = search_bilibili_videos(keyword)
    if not videos:
        print("搜索失败或未找到相关视频！")
        return
    # 显示搜索结果
    display_videos(videos)
    # 选择下载
    while True:
        try:
            choice = input("\n请输入要下载的视频序号 (输入 'q' 退出): ").strip()
            if choice.lower() == 'q':
                print("再见！")
                break
            index = int(choice) - 1
            selected_video = ""
            if 0 <= index < len(videos):
                selected_video = videos[index]
                print(f"\n你选择了: {selected_video['title']}")
                pagevideohelper(selected_video["aid"])
            else:
                print("序号无效，请重新输入！")
                
        except ValueError:
            print("请输入有效的数字！")
        except KeyboardInterrupt:
            print("\n用户中断操作")
            break

def load_star():
    uids=[]
    import json
    with open("star.json","r",encoding="utf-8") as stars:
        content = stars.read()
        starups=json.loads(content)
    for i in range(len(starups["ups"])):
        print(str(i+1)+"."+starups["ups"][i]["name"])
        uids.append(starups["ups"][i]["uid"]) 
    choice = int(input("请输入要选择的用户"))
    display_up_videos(uids[choice-1])

if __name__ == "__main__":
    password = input("请输入密码")
    if password == "ymfyeuygui":
        pass
    else:
        print("密码错误")
      #  exit(114514)
    os.system("rm output.mp4")
    #is_night_time()
    #if is_night_time()==False:
     #   print("仅供22:00-8:00使用")
#        exit(114514)
      #  pass
    while True:
        print("1.搜索视频")
        print("2.搜索用户")
        print("3.进入收藏夹")
        funcno=int(input())
        if funcno==1:
            searchvideo()
        elif funcno==2:
            searchup()
        else:
            load_star()
